﻿using System;
using System.Collections.Generic;

namespace Framework.ObjectPool
{
    public class ObjectPool<T> : ObjectPoolBase, IObjectPool<T>  where T : ObjectBase, new()
    {
        private const int DefaultCapatity = 5;
        private const float DefaultExpireTime = float.MaxValue;
        private const float DefaultAutoReleaseInterval = 0.5f;

        private string m_Name;
        private LinkedList<T> objects = null;
        private float m_AutoReleaseInterval;
        private float m_ExpireTime;
        private int m_Capatity;
        private float timer = 0;

        /// <summary>
        /// 获取对象池的名称
        /// </summary>
        public string Name
        {
            get
            {
                return m_Name;
            }
        }
        /// <summary>
        /// 获取对象池中所有对象数量
        /// </summary>
        public int Count
        {
            get
            {
                return objects.Count;
            }
        }
        /// <summary>
        /// 获取对象池中可以释放的对象数量
        /// </summary>
        public int CanReleaseCount
        {
            get
            {
                return GetCanReleaseObject().Count;
            }
        }
        /// <summary>
        /// 获取或设置对象池自动释放可释放对象的间隔秒数
        /// </summary>
        public float AutoReleaseInterval
        {
            get
            {
                return m_AutoReleaseInterval;
            }
            set
            {
                m_AutoReleaseInterval = value;
            }
        }
        /// <summary>
        /// 获取或设置对象池中对象过期描述
        /// </summary>
        public float ExpireTime
        {
            get
            {
                return m_ExpireTime;
            }
            set
            {
                m_ExpireTime = value;
            }
        }
        /// <summary>
        /// 获取或设置对象池的容量
        /// </summary>
        public int Capacity
        {
            get
            {
                return m_Capatity;
            }
            set
            {
                m_Capatity = value;
            }
        }

        /// <summary>
        /// 创建对象池
        /// </summary>
        /// <param name="name">对象池名称</param>
        public ObjectPool(string name)
            : this(name, DefaultCapatity, DefaultExpireTime, DefaultAutoReleaseInterval)
        {

        }
        /// <summary>
        /// 创建对象池
        /// </summary>
        /// <param name="name">对象池名称</param>
        /// <param name="expireTime">对象池的有效时间</param>
        public ObjectPool(string name, float expireTime)
            : this(name, DefaultCapatity, expireTime, DefaultAutoReleaseInterval)
        {

        }
        /// <summary>
        /// 创建对象池
        /// </summary>
        /// <param name="name">对象池名称</param>
        /// <param name="capatity">对象池容量</param>
        public ObjectPool(string name, int capatity)
            : this(name, capatity, DefaultExpireTime, DefaultAutoReleaseInterval)
        {

        }
        /// <summary>
        /// 创建对象池
        /// </summary>
        /// <param name="name">对象池名称</param>
        /// <param name="capatity">对象池容量</param>
        /// <param name="expireTime">对象池的有效时间</param>
        public ObjectPool(string name, int capatity, float expireTime)
            : this(name, capatity, expireTime, DefaultAutoReleaseInterval)
        {

        }
        /// <summary>
        /// 创建对象池
        /// </summary>
        /// <param name="name">对象池名称</param>
        /// <param name="capatity">对象池容量</param>
        /// <param name="expiretime">对象池的有效时间</param>
        /// <param name="autoReleaseInterval">对象池的自动释放时间间隔</param>
        public ObjectPool(string name, int capatity, float expiretime, float autoReleaseInterval)
        {
            objects = new LinkedList<T>();
            m_Name = name;
            m_Capatity = capatity;
            m_ExpireTime = expiretime;
            m_AutoReleaseInterval = autoReleaseInterval;
            timer = 0;
            for (int i = 0; i < m_Capatity; i++)
            {
                T obj = new T();
                obj.Name = name;
                Register(obj);
            }
        }
        /// <summary>
        /// 创建对象
        /// </summary>
        /// <returns>创建的对象</returns>
        public T Spawn()
        {
            return Spawn(string.Empty);
        }
        /// <summary>
        /// 创建对象
        /// </summary>
        /// <param name="name">要创建的对象的名称</param>
        /// <returns>创建的对象</returns>
        public T Spawn(string name)
        {
            LinkedListNode<T> current = objects.First;
            while(current != null)
            {
                if(current.Value.IsInUse == false)
                {
                    current.Value.IsInUse = true;
                    current.Value.LastUseTime = DateTime.Now;
                    current.Value.OnSpawn();
                    return current.Value;
                }
                current = current.Next;
            }
            T obj = new T();
            obj.Name = name;
            Register(obj);
            obj.IsInUse = true;
            obj.LastUseTime = DateTime.Now;
            obj.OnSpawn();
            return obj;
        }
        /// <summary>
        /// 创建对象
        /// </summary>
        /// <param name="packageName">资源包名称</param>
        /// <param name="resName">资源名称</param>
        /// <returns>创建的对象</returns>
        public T Spawn(string packageName, string resName)
        {
            LinkedListNode<T> current = objects.First;
            while (current != null)
            {
                if (current.Value.IsInUse == false)
                {
                    current.Value.IsInUse = true;
                    current.Value.LastUseTime = DateTime.Now;
                    current.Value.OnSpawn();
                    return current.Value;
                }
                current = current.Next;
            }
            T obj = new T();
            obj.Name = resName;
            obj.ResName = resName;
            obj.PackageName = packageName;
            Register(obj);
            obj.IsInUse = true;
            obj.LastUseTime = DateTime.Now;
            obj.OnSpawn();
            return obj;
        }
        /// <summary>
        /// 注册对象到对象池中
        /// </summary>
        /// <param name="obj">要注册的对象</param>
        public void Register(T obj)
        {
            if (obj == null || objects.Contains(obj))
            {
                return;
            }
            obj.IsInUse = false;
            obj.OnRegister();
            objects.AddLast(obj);
        }
        /// <summary>
        /// 回收对象
        /// </summary>
        /// <param name="obj">要回收的内部对象</param>
        public void Collect(T obj)
        {
            if (obj == null)
            {
                return;
            }
            obj.OnCollect();
            obj.IsInUse = false;
            if(!objects.Contains(obj))
            {
                objects.AddLast(obj);
            }
        }
        /// <summary>
        /// 回收对象
        /// </summary>
        /// <param name="obj">要回收的对象</param>
        public void Collect(object obj)
        {
            if(obj == null)
            {
                return;
            }
            LinkedListNode<T> current = objects.First;
            while(current != null)
            {
                if(current.Value.TargetObject == obj)
                {
                    current.Value.IsInUse = false;
                    current.Value.OnCollect();
                    return;
                }
                current = current.Next;
            }
        }
        /// <summary>
        /// 释放所有的空闲对象
        /// </summary>
        public void ReleaseFreeObject()
        {
            LinkedListNode<T> current = objects.First;
            while(current != null)
            {
                LinkedListNode<T> next = current.Next;
                if(!current.Value.IsInUse)
                {
                    current.Value.OnRelease();
                    objects.Remove(current);
                }
                current = next;
            }
        }
        /// <summary>
        /// 释放所有对象
        /// </summary>
        public void ReleaseAllObject()
        {
            LinkedListNode<T> current = objects.First;
            while(current != null)
            {
                current.Value.OnRelease();
                current = current.Next;
            }
            objects.Clear();
        }
        /// <summary>
        /// 关闭对象池
        /// </summary>
        public override void Destroy()
        {
            ReleaseAllObject();
        }
        /// <summary>
        /// 对象池更新方法
        /// </summary>
        /// <param name="frameTime">逻辑帧时间</param>
        /// <param name="realTime">实际上一帧的时间</param>
        public override void Update(float frameTime, float realTime)
        {
            timer += realTime;
            if(timer >= m_AutoReleaseInterval)
            {
                LinkedListNode<T> current = objects.First;
                while(current != null)
                {
                    double times = (DateTime.Now - current.Value.LastUseTime).TotalSeconds;
                    if(current.Value.IsInUse && times >= m_ExpireTime)
                    {
                        Collect(current.Value);
                    }
                    current = current.Next;
                }
                timer = 0;
            }
        }
        
        private LinkedList<T> GetCanReleaseObject()
        {
            LinkedList<T> releaseObjects = new LinkedList<T>();
            LinkedListNode<T> current = objects.First;
            while(current != null)
            {
                if(!current.Value.IsInUse)
                {
                    releaseObjects.AddLast(current.Value);
                }
                current = current.Next;
            }
            return releaseObjects;
        }
    }
}
